package com.qingzhu.component.lock.starter.config;

import com.qingzhu.component.lock.annotation.LockCommonBeanAnnotation;
import com.qingzhu.component.lock.annotation.LockTypeBeanAnnotation;
import com.qingzhu.component.lock.annotation.strategy.LockCacheOutStrategyBean;
import com.qingzhu.component.lock.common.constant.AutoConfig;
import com.qingzhu.component.lock.starter.scanner.LockCacheOutStrategyBeanDefinitionScanner;
import com.qingzhu.component.lock.starter.scanner.LockComponentBeanDefinitionScanner;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.core.type.filter.AnnotationTypeFilter;

import java.lang.annotation.Annotation;
import java.util.List;

import static java.util.Arrays.asList;

/**
 * lock相关组件的bd注册器
 * @author xiangjz
 * @version 1.0
 * @date 2021/1/15 9:54
 */
@Slf4j
@ConditionalOnProperty(prefix = AutoConfig.CONFIG_PREFIX, name = "enable", havingValue = "true")
@AutoConfigureBefore(LockComponentAutoConfiguration.class)
public class LockComponentBeanDefinitionRegisterer implements BeanDefinitionRegistryPostProcessor,
        EnvironmentAware,
        BeanClassLoaderAware {

    private Environment environment;

    private ClassLoader classLoader;

    private final static List<Class<? extends Annotation>> serviceAnnotationTypes = asList(
            LockCommonBeanAnnotation.class
    );

    private final static List<Class<? extends Annotation>> lockTypeBeanAnnotationTypes = asList(
            LockTypeBeanAnnotation.class
    );

    private final static List<Class<? extends Annotation>> lockCacheOutStrategyBeanAnnotationTypes = asList(
            LockCacheOutStrategyBean.class
    );

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        // 搜寻 LockCommonBeanAnnotation 注解的所有bean
        LockComponentBeanDefinitionScanner scanner = new LockComponentBeanDefinitionScanner(registry,
                this.environment, this.classLoader, lockTypeBeanAnnotationTypes);
        LockCacheOutStrategyBeanDefinitionScanner scanner2 = new LockCacheOutStrategyBeanDefinitionScanner(registry,
                this.environment, this.classLoader, lockCacheOutStrategyBeanAnnotationTypes);
        serviceAnnotationTypes.forEach(annotationType -> {
            scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
        });

        // 调用scan方法，将所有匹配的类加入容器
        scanner.scan(AutoConfig.COMPONENT_PACKAGE_PATH);
        scanner2.addIncludeFilter(new AnnotationTypeFilter(LockCacheOutStrategyBean.class));
        scanner2.scan(AutoConfig.COMPONENT_PACKAGE_PATH);
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    /**
     * 自定义的BeanNameGenerator
     * 继承自AnnotationBeanNameGenerator
     * 主要是为了自定义beanName，同时能够让scanner判断不重复beanName
     * 因为这个组件业务上不允许重复bean注册到容器
     */
    /*private class LockComponentAnnotationBeanNameGenerator extends AnnotationBeanNameGenerator {
        @Override
        protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
            AnnotationMetadata amd = annotatedDef.getMetadata();
            Set<String> types = amd.getAnnotationTypes();
            String beanName = null;
            for (String type : types) {
                AnnotationAttributes attributes = AnnotationAttributes.fromMap(amd.getAnnotationAttributes(type, false));
                if (attributes != null && isStereotypeWithNameValue(type, amd.getMetaAnnotationTypes(type), attributes)) {
                    Object value = attributes.get("value");
                    if (value instanceof String) {
                        String strVal = (String) value;
                        if (StringUtils.hasLength(strVal)) {
                            if (beanName != null && !strVal.equals(beanName)) {
                                throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
                                        "component names: '" + beanName + "' versus '" + strVal + "'");
                            }

                            beanName = strVal + annotatedDef.getBeanClassName();
                        }
                    }
                }
            }
            return beanName;
        }

        @Override
        protected boolean isStereotypeWithNameValue(String annotationType, Set<String> metaAnnotationTypes, Map<String, Object> attributes) {
            boolean isStereotype = annotationType.equals(AutoConfig.COMPONENT_ANNOTATION_PATH);

            return (isStereotype && attributes != null && attributes.containsKey("value"));
        }
    }*/

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }

}
